package com.example.blog.modules.log.aspect;

import com.example.blog.modules.log.annotation.Log;
import com.example.blog.modules.log.dto.OperationLogDTO;
import com.example.blog.modules.log.service.OperationLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class LogAspect {
    
    private final OperationLogService operationLogService;
    private final ObjectMapper objectMapper;
    
    @Around("@annotation(com.example.blog.modules.log.annotation.Log)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        
        try {
            // 执行方法
            Object result = point.proceed();
            // 执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            // 保存日志
            saveLog(point, time, true, null);
            return result;
        } catch (Throwable e) {
            // 执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            // 保存日志
            saveLog(point, time, false, e.getMessage());
            throw e;
        }
    }
    
    private void saveLog(ProceedingJoinPoint joinPoint, long time, boolean success, String errorMessage) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);
        
        OperationLogDTO logDTO = new OperationLogDTO();
        
        // 设置用户名
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            logDTO.setUsername(authentication.getName());
        }
        
        // 设置操作
        logDTO.setOperation(logAnnotation.operation());
        logDTO.setMethod(method.getDeclaringClass().getName() + "." + method.getName());
        
        // 设置请求参数
        if (logAnnotation.saveParams()) {
            try {
                String params = objectMapper.writeValueAsString(joinPoint.getArgs());
                logDTO.setParams(params);
            } catch (Exception e) {
                log.error("记录操作日志时，序列化请求参数失败", e);
            }
        }
        
        // 设置请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            logDTO.setIp(getClientIp(request));
            logDTO.setUserAgent(request.getHeader("User-Agent"));
            logDTO.setRequestUrl(request.getRequestURI());
        }
        
        // 设置执行结果
        logDTO.setExecutionTime(time);
        logDTO.setDescription(logAnnotation.value());
        logDTO.setSuccess(success);
        logDTO.setErrorMessage(errorMessage);
        
        // 保存日志
        operationLogService.save(logDTO);
    }
    
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
} 